#include "lm_macro.h"
#include <string.h>
#include <stdlib.h>
#include "lm_string.h"
#include "../common/lm_error.h"
#include "../common/lm_mem.h"


lm_macro_t* lm_macro_get_menu_first_macro(lm_macro_t *menu)
{
    lm_list_node_t *node = &menu->head;
    node = lm_list_next_node(node);
    return container_of(node, lm_macro_t, node);
}


lm_macro_t* lm_macro_get_menu_last_macro(lm_macro_t *menu)
{
    lm_list_node_t *node = &menu->head;
    node = lm_list_prev_node(node);
    return container_of(node, lm_macro_t, node);
}


lm_macro_t* lm_macro_list_get_next(lm_macro_t *macro)
{
    lm_list_node_t *node = &macro->node;
    node = lm_list_next_node(node);
    return container_of(node, lm_macro_t, node);
}


lm_macro_t* lm_macro_list_get_prev(lm_macro_t *macro)
{
    lm_list_node_t *node = &macro->node;
    node = lm_list_prev_node(node);
    return container_of(node, lm_macro_t, node);
}


void lm_macro_list_add(lm_macro_t *menu, lm_macro_t *macro)
{
    menu->count ++;
    lm_list_add_node_at_tail(&menu->head, &macro->node);
}


lm_macro_t* lm_macro_new(char* name)
{
    lm_macro_t* macro = lm_malloc(sizeof(lm_macro_t));
    if(macro == NULL) {
        return NULL;
    }

    macro->parent = NULL;
    macro->name = NULL;
    macro->menu = false;
    macro->count = 0;
    macro->description = NULL;
    macro->choice.count = 0;
    lm_list_init(&macro->choice.head);
    macro->depend = NULL;
    macro->enable = false;
    macro->value = NULL;

    int ret = lm_str_dupli_string(&macro->name, name);
    if(ret) {
        return LM_ERR_PTR;
    }

    return macro;
}


lm_macro_t* lm_macro_new_and_add(lm_macro_t *menu, char* name)
{
    lm_macro_t* macro = lm_macro_new(name);
    if(macro == NULL) {
        return NULL;
    }

    if(menu != NULL)
        lm_macro_list_add(menu, macro);
    else
        macro->parent = macro;

    return macro;
}


lm_macro_t* lm_macro_add_menu(lm_macro_t *menu, char *desc)
{
    lm_macro_t* macro = lm_macro_new_and_add(menu, desc);
    if(macro == NULL) {
        return NULL;
    }

    macro->menu = true;
    lm_list_init(&macro->head);
    return macro;
}


void lm_macro_parent_set(lm_macro_t *macro, lm_macro_t *parent)
{
    macro->parent = parent;
}


lm_macro_t *lm_macro_parent_get(lm_macro_t *macro)
{
    return macro->parent;
}


int lm_macro_desc_add(lm_macro_t* macro, char *desc)
{
    return lm_str_dupli_string(&macro->description, desc);
}


int lm_macro_value_add(lm_macro_t* macro, char *value)
{
    if(value == NULL) {
        macro->value = NULL;
        return LM_OK;
    }
    return lm_str_dupli_string(&macro->value, value);
}


void lm_macro_enable_set(lm_macro_t* macro, bool flag)
{
    macro->enable = flag;
}


int lm_array_add_string(lm_array_t *array, char *str)
{
    lm_array_node_t *node = lm_malloc(sizeof(lm_array_node_t));
    if(node == NULL) {
        return LM_ERR;
    }

    node->string = NULL;

    int ret = lm_str_dupli_string(&node->string, str);
    if(ret) {
        lm_free(node);
        return LM_ERR;
    }

    lm_list_add_node_at_tail(&array->head, &node->node);
    array->count ++;

    return 0;
}


int lm_array_delete(lm_array_t *array)
{
    if(array->count == 0) {
        return LM_OK;
    }

    lm_list_node_t *node = lm_list_next_node(&array->head);

    for(int i = 0; i < array->count; i++) {
        
        lm_array_node_t *array_node = container_of(node, lm_array_node_t, node);
        lm_free(array_node->string);
        lm_free(array_node);

        node = lm_list_next_node(node);
    }
    return LM_OK;
}


int lm_macro_choice_add(lm_macro_t* macro, char* str)
{
    return lm_array_add_string(&macro->choice, str);
}


char *lm_macro_choice_get_first(lm_macro_t* macro)
{
    lm_list_node_t *node = lm_list_next_node(&macro->choice.head);
    lm_array_node_t *array_node = NULL;

    array_node = container_of(node, lm_array_node_t, node);

    return array_node->string;
}


void lm_macro_choice_set(lm_macro_t* macro, lm_array_t array)
{
    macro->choice = array;
}


int lm_macro_depend_add(lm_macro_t* macro, char* str)
{
    return lm_str_dupli_string(&macro->depend, str);
}


char* lm_macro_depend_get(lm_macro_t* macro)
{
    return macro->depend;
}


int lm_macro_description_add(lm_macro_t* macro, char* str)
{
    return lm_str_dupli_string(&macro->description, str);
}


int lm_macro_delete(lm_macro_t* macro)
{
    int ret = lm_array_delete(&macro->choice);
    if(ret != LM_OK) {
        return LM_ERR;
    }

    lm_free(macro->depend);
    lm_free(macro->name);
    lm_free(macro->description);
    lm_free(macro);

    return LM_OK;
}


int lm_macro_list_delete(lm_macro_t *menu)
{
    lm_list_node_t *head = &menu->head;
    lm_list_node_t *node;
    lm_macro_t *macro = NULL;

    lm_list_for_each(node, head) {

        macro = container_of(node, lm_macro_t, node);
        if(macro == NULL) {
            return LM_ERR;
        }

        lm_macro_delete(macro);
    }
    return LM_OK;
}


lm_macro_t *lm_macro_search_by_name(lm_macro_t *menu, char *macro_name)
{
    lm_macro_t *ret = NULL;
    if(menu == NULL) {
        return NULL;
    }
    
    lm_list_node_t *head = &menu->head;
    lm_list_node_t *node;
    lm_macro_t *macro = NULL;

    lm_list_for_each(node, head) {

        macro = container_of(node, lm_macro_t, node);
        if(macro == NULL) {
            return NULL;
        }

        if(macro->menu) {
            ret = lm_macro_search_by_name(macro, macro_name);
            if(ret != NULL) {
                return ret;
            }
        }
        
        if(strcmp(macro->name, macro_name) == 0) {
            return macro;
        }
    }

    return NULL;
}


char *lm_macro_get_description(lm_macro_t *menu, char *macro_name)
{
    lm_macro_t *macro = lm_macro_search_by_name(menu, macro_name);
    if(macro == NULL) {
        return NULL;
    }
    
    return macro->description;
}


lm_macro_t *lm_macro_get_parent(lm_macro_t* macro)
{
    return macro->parent;
}


void lm_array_print( FILE* output, lm_array_t *array)
{
    lm_list_node_t *node = lm_list_next_node(&array->head);
    lm_array_node_t *array_node = NULL;

    for(int i = 0; i < array->count; i++) {
        
        array_node = container_of(node, lm_array_node_t, node);

        if(array_node != LM_ERR_PTR) {
            fprintf(output, "%s ", array_node->string);
        }

        node = lm_list_next_node(node);
    }
    
    fprintf(output, "\n");
}


char pre[256];
void lm_macro_print_macro(FILE* output, lm_macro_t *macro, int prefix)
{
    int i = 0;

    fprintf(output, "\n");

    if(!macro->menu) {
        for(; i < prefix; i++) {
            pre[i] = ' ';
        }
        pre[i] = '\0';

        fprintf(output, "%s|%s:\n", pre, macro->name);
        fprintf(output, "%s|    description: %s\n", pre, macro->description);
        fprintf(output, "%s|    choice: ", pre); lm_array_print(output, &macro->choice);
        fprintf(output, "%s|    value: %s\n", pre, macro->value);
        fprintf(output, "%s|    depend: %s\n", pre, macro->depend);
    }
    else {
        for(; i < prefix; i++) {
            pre[i] = '-';
        }
        pre[i] = '\0';

        fprintf(output, "%s%s:\n", pre, macro->name);
    }
}

void lm_macro_print_all_attr(lm_macro_t *menu, FILE* output)
{
    static int prefix = 0;

    lm_list_node_t *head = &menu->head;
    lm_list_node_t *node;
    lm_macro_t *macro = NULL;

    lm_list_for_each(node, head) {

        macro = container_of(node, lm_macro_t, node);
        if(macro == NULL) {
            return;
        }

        lm_macro_print_macro(output, macro, prefix);

        if(macro->menu) {

            prefix += 4;

            lm_macro_print_all_attr(macro, output);
        }
    }
    prefix -= 4;
}
